/* Copyright (c) 2008 Charles Rich and Worcester Polytechnic Institute.
 * All Rights Reserved.  Use is subject to license terms.  See the file
 * "license.terms" for information on usage and redistribution of this
 * file and for a DISCLAIMER OF ALL WARRANTIES.
 */
package edu.wpi.cetask;

import java.util.*;

import javax.script.*;

/**
 * Representation for instances of a decomposition class.
 */
public class Decomposition extends Instance {
   
   Decomposition (DecompositionClass type, Task goal) { 
      super(type, goal.engine);
      if ( goal == null ) 
         throw new IllegalArgumentException("Goal of decomposition cannot be null");
      this.goal = goal;
      bindings.put("$this", goal.bindings.get("$this"));
   }
   
   @Override
   public DecompositionClass getType() { 
      return (DecompositionClass) super.getType(); 
   }
   
   private final Task goal;
   public Task getGoal () { return goal; }
   
   private boolean failed;
   void setFailed (boolean failed) { this.failed = failed; }
   
   //TODO may be better to store plans here (and make subtype Plan.Step)
   //     to avoid needing to call Plan#childWithGoal
   
   private final Map<String,Step> steps = new HashMap<String,Step>();
   
   void putStep (String name, Step step) { 
      steps.put(name, step); 
      bindings.put('$'+name, step.bindings.get("$this"));
   }
   
   public Step getStep (String name) { return steps.get(name); }
   
   public Collection<Step> getSteps () { return steps.values(); }
   
   private boolean modified = true; // to reduce calls to updateBindings
 
   public class Step extends Task {
      
      private final String name; 
      
      public String getName () { return name; }
      
      Step (TaskClass type, String name) { 
         super(type, type.engine);
         this.name = name;
      }

      public Decomposition getDecomposition () { return Decomposition.this; }
      
      @Override
      protected Object eval (String expression, String where) {
         updateBindings();
         return super.eval(expression, where);
      }
      
      @Override
      protected Object eval (String script, Bindings extra, String where) 
             throws ScriptException {
         updateBindings();
         return super.eval(script, extra, where);
      }
      
      private void updateBindings () {
         if ( !failed && 
               (modified || goal.isModified()) && !TaskEngine.updatingBindings ) 
            try {
               TaskEngine.updatingBindings = true;
               Decomposition.this.getType().updateBindings(Decomposition.this);
               modified = false;
               goal.setModified(false);
            } finally { TaskEngine.updatingBindings = false; }
      }
      
      // only setting slot values sets modified bit; evaluation of conditions, etc.
      // is specified not to have side effects

      @Override
      public void setSlotValue (String name, Object value) {
         modified = true;
         super.setSlotValue(name, value);
      }
      
      @Override
      public void setSlotValueScript (String name, String expression, String where) { 
         modified = true;
         super.setSlotValueScript(name, expression, where);
      }
      
      @Override
      void setSlotValueScript (String name, String expression, String where,
            Bindings extra) {
         modified = true;
         super.setSlotValueScript(name, expression, where, extra);
      }
      
      @Override
      public void deleteSlotValue (String name) {
         modified = true;
         super.deleteSlotValue(name);
      }
   }
}

